import { createElm } from "./index";
import { isUndef } from "../help";
import { hooks } from "./hooks/index";
import { insertBefore, removeChild, setTextContent } from "./node-ops";
function invokeUpdateHooks(old, vnode){
    for (let i=0; i<hooks.update.length; i++){
        hooks.update[i](old, vnode);
    }
}
function addVnodes(elm, vnodes){
    vnodes.forEach(vnode=>{
        createElm(vnode);
        appendChild(elm, vnode.elm);
    });
}
function removeVnodes(elm, vnodes){
    vnodes.forEach(vnode=> vnode && removeChild(elm, vnode.elm));
}
function sameNode(a, b){
    return a.tag === b.tag;
}
function getIndexFromOld(oldCh, vnode){
    for (let i=0;i<oldCh.length; i++){
        if (!isUndef(oldCh[i]) && sameNode(oldCh[i], vnode)){
            return i;
        }
    }
}
function updateChildren(elm, oldCh, ch){
    // 参考点, 就新dom全部插在 所有老节点的前面, 好处是可以方便移除不需要的
    // 新1 新2 新3 | 旧A 旧B
    /*
    这里的对比还是新节点在老节点里看看有没有相同的, 时间复杂度 n^2
    实际可以增加预判, 让新老节点的头尾对比看看可以复用, 如果都不可以, 再去 遍历子节点。
    */
    let oldStartIndex = 0;
    let refNode = oldCh[oldStartIndex];
    let newStartIndex = 0;
    let oldEndIndex = oldCh.length - 1;
    let oldEndNode = oldCh[oldEndIndex];
    let newEndIndex = ch.length - 1;
    let newEndNode = ch[newEndIndex];
    for (let i=0; i<ch.length; i++){
        // 用于记录处理的个数, 要是比 ch.length-1 少, 说明还有新节点需要重新创建
        newStartIndex ++;
        const vnode = ch[i];
        if (oldStartIndex > oldCh.length-1){
            break;
        }
        
        if (sameNode(vnode, refNode)){
            // 头与头对比
            /*
            其实就是下面 if (moveIndex===oldStartIndex)的特例情况
            */
            patchVnode(refNode, vnode); 
            oldCh[oldStartIndex] = undefined;
            oldStartIndex++;
            refNode = oldCh[oldStartIndex];  
        }else if (sameNode(oldEndNode, newEndNode)){
            // 尾与尾对比
            patchVnode(oldEndNode, newEndNode);
            oldCh[oldEndIndex] = undefined;
            oldEndNode = oldCh[--oldEndIndex];
            newEndNode = ch[--newEndIndex]
            
        }else if (sameNode(vnode, oldEndNode)){
            // 新头旧尾对比
            patchVnode(oldEndNode, vnode);
            oldCh[oldEndIndex] = undefined;
            oldEndNode = oldCh[--oldEndIndex];
            insertBefore(elm, oldEndNode.elm, refNode.elm);
        }else if (sameNode(newEndNode, refNode)){
            patchVnode(refNode, newEndNode);
            oldCh[oldStartIndex] = undefined;
            oldStartIndex++;
            refNode = oldCh[oldStartIndex];
            newEndNode = ch[--newEndIndex];
            insertBefore(elm, refNode.elm, refNode.elm.nextSibling);

        }else{
            // 去老节点找可以复用的
            const moveIndex = getIndexFromOld(oldCh, vnode);
            if (isUndef(moveIndex)){
                createElm(vnode);
                console.log('create', vnode);
                insertBefore(elm, vnode.elm, refNode.elm);
            }else{
                const moveVnode = oldCh[moveIndex];
                // 本层不用重新生成dom
                patchVnode(moveVnode, vnode);
                // 复用元素不能复用第二次, 这样设置 getIndexFromOld 就找不到这个元素了。
                oldCh[moveIndex] = undefined;
                if (moveIndex===oldStartIndex){
                    // 更新参考点
                    oldStartIndex ++;
                    // 新1 新2 新3 | 旧A 旧B
                    refNode = oldCh[oldStartIndex];
                }else{
                    insertBefore(elm, moveVnode.elm, refNode.elm);
                }
            }
        }
        
        
    }

    addVnodes(elm, ch.slice(newStartIndex+1))
    removeVnodes(elm, oldCh);
}
export function patchVnode(old, vnode){
    const elm = (vnode.elm = old.elm);
    // 更新props,class, attr，事件之类的
    if (vnode.data || old.data){
        invokeUpdateHooks(old, vnode);
    }
    let i;
    
    console.log(old)
    if (!isUndef(i=old.data) && !isUndef(i=i.hooks) && !isUndef(i=i.prepatch)){
        i(old, vnode);
    }
    // 子节点
    const ch = vnode.children;
    const oldCh = old.children;
    /*
    四种情况 
    1. ch 有 , oldCh 没有 创建即可
    2. ch 没有 , oldCh 有 删除即可
    3. ch 有 , oldCh 有   diff对比
    4. ch 没有 , oldCh 没有 不用做
    */
    if (isUndef(vnode.text)){
        // 新的是元素节点，老的可能是元素节点，也可能是文本节点
        if(ch && oldCh){
            // 新老都是元素节点
            if (ch !== oldCh){
                updateChildren(elm, oldCh, ch);
            }
        }else if (!isUndef(oldCh)){
            // 老节点是元素节点，新节点节点是空
            console.log('移除节点');
            removeVnodes(elm, oldCh);
        }else if (!isUndef(ch)){
            // 新节点是元素节点，老节点是空
            console.log('新增节点');
            addVnodes(elm, ch);
        }else if (!isUndef(old.text)){
            // 老节点是文本节点, 新节点是空
            console.log('设置文本1');
            setTextContent(elm, '');
        }
    }else if (vnode.text !== old.text){
        // 新的是文本节点, 所以只要把当前节点替换成文本节点就好
        console.log('设置文本2');
        setTextContent(elm, vnode.text);
    }
    
}